package com.md.util;

import lombok.extern.slf4j.Slf4j;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import org.joda.time.DateTime;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.messaging.context.SAMLEndpointContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameIDPolicy;
import org.opensaml.saml.saml2.core.NameIDType;
import org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder;
import org.opensaml.saml.saml2.core.impl.IssuerBuilder;
import org.opensaml.saml.saml2.core.impl.NameIDPolicyBuilder;
import org.opensaml.saml.saml2.metadata.Endpoint;
import org.opensaml.saml.saml2.metadata.SingleSignOnService;
import org.opensaml.saml.saml2.metadata.impl.SingleSignOnServiceBuilder;
import org.opensaml.xmlsec.context.SecurityParametersContext;
import org.opensaml.xmlsec.signature.Signature;
import org.opensaml.xmlsec.signature.support.SignatureConstants;

import javax.xml.transform.TransformerException;
import java.util.UUID;

/**
 * @author 刘骄阳
 * 2024-03-11 23:46
 */
@Slf4j
public class SamlUtil {
  private static Endpoint getIPDEndpoint() {
    SingleSignOnService endpoint = new SingleSignOnServiceBuilder().buildObject();
    endpoint.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
    // 目标地址
    endpoint.setLocation("https://login.microsoftonline.com/00618cc4-0e79-43ef-9df6-41ddc9a38d62/saml2");

    return endpoint;
  }

  /**
   * @param idpSsoUrl  目标地址，即 IDP 接收请求的地址
   * @param acsUrl     回调地址
   * @param spEntityId 应用ID
   * @return SamlRequest
   * @throws TransformerException
   * @throws MarshallingException
   */
  public static String createRequestXmlString(final String idpSsoUrl,
                                              final String acsUrl,
                                              final String spEntityId) throws Exception {
    AuthnRequest authnRequest = createRequest(idpSsoUrl, acsUrl, spEntityId);
    HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
    MessageContext<SAMLObject> context = new MessageContext<>();
    context.setMessage(authnRequest);
//    authnRequest.setSignature();


    //关于传输对端实体的信息，对于IDP就是SP，对于SP就是IDP；
    SAMLPeerEntityContext peerEntityContext =
      context.getSubcontext(SAMLPeerEntityContext.class, true);

    //端点信息；
    SAMLEndpointContext endpointContext =
      peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
    endpointContext.setEndpoint(getIPDEndpoint());

//    //数据签名环境上线文
//    SignatureSigningParameters signatureSigningParameters = new SignatureSigningParameters();
//    //获得证书，其中包含公钥
//    signatureSigningParameters.setSigningCredential(SPCredentials.getCredential());
//    //ALGO_ID_SIGNATURE_RSA_SHA256
//    signatureSigningParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);

    context.getSubcontext(SecurityParametersContext.class, true);
//        .setSignatureSigningParameters(signatureSigningParameters);


    encoder.setMessageContext(context);
    encoder.setHttpServletResponse(HttpUtil.getHttpServletResponse());


    try {
      encoder.initialize();
      //*encode*方法将会压缩消息，生成签名，添加结果到URL并从定向用户到Idp.
      //先使用RFC1951作为默认方法压缩数据，在对压缩后的数据信息Base64编码
      encoder.encode();
    } catch (ComponentInitializationException e) {
      throw new RuntimeException(e);
    }
    return "";
  }

  /**
   * 创建AutheRequest对象
   *
   * @param idpSsoUrl  目标地址，即 IDP 接收请求的地址
   * @param acsUrl     回调地址
   * @param spEntityId 应用ID
   * @return
   */
  public static AuthnRequest createRequest(final String idpSsoUrl, final String acsUrl, final String spEntityId) {
    AuthnRequestBuilder authnRequestBuilder = new AuthnRequestBuilder();

    // 使用Builder设置属性
    AuthnRequest authnRequest = authnRequestBuilder.buildObject();
    // 如果需要签名（此处仅为示例，实际签名过程会更复杂）
    Signature signature = (Signature) XMLObjectProviderRegistrySupport.getBuilderFactory()
      .getBuilder(Signature.DEFAULT_ELEMENT_NAME)
      .buildObject(Signature.DEFAULT_ELEMENT_NAME);
//    signature.setSigningCredential(""); // 设置签名凭据
    signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);

    // 将签名添加到AuthnRequest中
    authnRequest.setSignature(signature);

    authnRequest.setIssueInstant(new DateTime());
    authnRequest.setDestination(idpSsoUrl);
    authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
    authnRequest.setID("A" + UUID.randomUUID());
    authnRequest.setAssertionConsumerServiceURL(acsUrl);


    IssuerBuilder issuerBuilder = new IssuerBuilder();
    Issuer issuer = issuerBuilder.buildObject();
    issuer.setValue(spEntityId);
    authnRequest.setIssuer(issuer);


    NameIDPolicyBuilder nameIDPolicyBuilder = new NameIDPolicyBuilder();
    NameIDPolicy nameIDPolicy = nameIDPolicyBuilder.buildObject();
    nameIDPolicy.setAllowCreate(true);
    nameIDPolicy.setFormat(NameIDType.UNSPECIFIED);
    authnRequest.setNameIDPolicy(nameIDPolicy);
    return authnRequest;
  }

}
